﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.ComponentModel;
using Microsoft.Xna.Framework.Input;
using BEPUphysics;
using BEPUphysics.Entities;
using The_LarX.Delegates;

namespace The_LarX
{
    public class PhysicChaseCamera : Camera3D
    {
        public float MovementSpeed
        {
            get { return speed; }
            set { speed = value; }
        }
        protected float speed = 30;

        public override Vector3 Position
        {
            get;
            set;
        }


        public Vector3 Forward { get { return FollowActor.RotationMatrix.Forward; } }

        //Camera Physics
        /// <summary>
        /// The maximum distance the Camera can go away from the Actor
        /// </summary>
        public float MaxDistance = 3;
        public float MinDistance = 2;

        public float Distance
        {
            get { return distance; }
        }
        public int FramesDivision = 15;
        protected float distance;
        public Vector3 PositionOffset { get; set; }
        public Vector3 TargetOffset { get; set; }
        protected I3DObject FollowActor;

        //public BepuEntity_Box Entity;

        public BoundingSphere BehindSphere;
        protected List<Entity> gotEntities;

        public PhysicChaseCamera(I3DObject actor, GameScreen Screen)
            : base(Screen)
        {
            FarPlane = 100;
            this.NearPlane = 0.05f;
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, FarPlane);

            Target = new Vector3(0, 0, 0);

            Actor.ComponentParent = this;

            NearPlane = 1;

            FollowActor = actor;

            //Entity = new BepuEntity_Box();

            distance = MaxDistance;

            BehindSphere = new BoundingSphere();
            gotEntities = new List<Entity>();
        }
        public PhysicChaseCamera()
        {
            FarPlane = 100;
            this.NearPlane = 0.05f;
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Engine.AspectRatio, 1.0f, FarPlane);

            Target = new Vector3(0, 0, 0);

            NearPlane = 1;
            FarPlane = 100;

            Actor.ComponentParent = this;
            //Entity = new BepuEntity_Box();

            distance = MaxDistance;

            BehindSphere = new BoundingSphere();
            gotEntities = new List<Entity>();            
        }

        public override void Update()
        {
            if (FarPlane == 0)
                FarPlane = 100;
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, AspectRatio, NearPlane, FarPlane);

            base.Update();
        }

        public override void Draw()
        {
            base.Draw();

            //Entity.Position = Position;
            UpdateViewMatrix();
        }

        public ChaseCameraHelperMethod SetMatrix;
        public ChaseCameraCustomRotationMatrix GetCustomRotationMatrix;
        public GetVector3 GetPosition;

        public Entity PlayerEntity;

        public int SphereRadius = 1;

        private void UpdateViewMatrix()
        {
            if (FollowActor is Actor)
            {
                Actor actor = FollowActor as Actor;
                actor.Draw();
                if (SetMatrix != null)
                    SetMatrix();
            }

            Matrix RotationMatrix;
            if (GetCustomRotationMatrix == null)
                RotationMatrix = FollowActor.RotationMatrix;
            else
                RotationMatrix = GetCustomRotationMatrix();

            bool IsBehind = false;
            gotEntities.Clear();
            BehindSphere.Center = this.Position;
            BehindSphere.Radius = SphereRadius;
            /*Entity.Entity.Space.BroadPhase.GetEntities(BehindSphere, gotEntities);
            if (gotEntities.Count > 0)
            {
                int count = gotEntities.Count;
                if (gotEntities.Contains(PlayerEntity))
                    count--;
                if (gotEntities.Contains(Entity.Entity))
                    count--;

                if (count > 0)
                    IsBehind = true;
            }*/

            if (IsBehind)
            {
                //distance = AnimationHelper.AnimateStatic(distance, MinDistance, FramesDivision);
                distance = AnimationHelper.AnimateStatic(distance, MaxDistance, FramesDivision);
            }
            else
            {
                distance = AnimationHelper.AnimateStatic(distance, MaxDistance, FramesDivision);
            }

            Vector3 posOffset = Vector3.Transform(PositionOffset, RotationMatrix);
            Vector3 campos = Vector3.Zero;

            Vector3 followActorPos;
            if (GetPosition != null)
                followActorPos = GetPosition();
            else
                followActorPos = FollowActor.Position;

            campos = Vector3.Normalize(posOffset) * distance;

            campos += followActorPos;
            Position = campos;

            Vector3 camup = new Vector3(0, 1, 0);
            camup = Vector3.TransformNormal(camup, RotationMatrix);

            Vector3 finalTargetOffset = Vector3.Transform(TargetOffset, RotationMatrix);
            View = Matrix.CreateLookAt(campos, followActorPos + finalTargetOffset, camup);
            this.Actor.Position = campos;
            this.Target = followActorPos + finalTargetOffset;
        }
        /// <summary>
        /// Translates a point around an origin
        /// </summary>
        /// <param name="point">Point that is going to be translated</param>
        /// <param name="originPoint">Origin of rotation</param>
        /// <param name="rotationAxis">Axis to rotate around, this Vector should be a unit vector (normalized)</param>
        /// <param name="radiansToRotate">Radians to rotate</param>
        /// <returns>Translated point</returns>
        public Vector3 RotateAroundPoint(Vector3 point, Vector3 originPoint, Vector3 rotationAxis, float radiansToRotate)
        {
            Vector3 diffVect = point - originPoint;

            Vector3 rotatedVect = Vector3.Transform(diffVect, Matrix.CreateFromAxisAngle(rotationAxis, radiansToRotate));

            rotatedVect += originPoint;

            return rotatedVect;
        }

        public override void SetToInitialState()
        {
            base.SetToInitialState();

            Actor.SetToInitialState();
        }
    }
}